﻿using Brc_PaaS.Common.Helper;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Brc_PaaS.Common.Model.Basic;
using Microsoft.Extensions.Configuration;

namespace Brc_PaaS.Core.Redis
{
    public class RedisManager : IRedisManager
    {
        private readonly RedisConfig redisConfig;

              
        public RedisManager(RedisServerEntity redisServerEntity)
        {            
            redisConfig = new RedisConfig(redisServerEntity);
        }
        public IDatabase database
        {
            get
            {
                return redisConfig.Instance.GetDatabase();
            }
        }

        public IConnectionMultiplexer Instance
        {
            get
            {
                return redisConfig.Instance;
            }
        }

        #region String
        


        /// <summary>
        /// 保存单个key value
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public bool StringSet(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {
            return database.StringSet(key, value, expiry);
        }

        /// <summary>
        /// 保存多个key value
        /// </summary>
        /// <param name="keyValues">键值对</param>
        /// <returns></returns>
        public bool StringSet(List<KeyValuePair<RedisKey, RedisValue>> keyValues)
        {
            List<KeyValuePair<RedisKey, RedisValue>> newkeyValues =
                keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(p.Key, p.Value)).ToList();
            return database.StringSet(newkeyValues.ToArray());
        }

        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool StringSet<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
        {
            string json = ConvertJson(obj);
            return database.StringSet(key, json, expiry);
        }

        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool StringSet<T>(string key, T obj, DateTime expiry)
        {
            string json = ConvertJson(obj);
            return database.StringSet(key, json, expiry - DateTime.Now);
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>
        public string StringGet(string key)
        {
            return database.StringGet(key);
        }

        /// <summary>
        /// 获取多个Key
        /// </summary>
        /// <param name="listKey">Redis Key集合</param>
        /// <returns></returns>
        public RedisValue[] StringGet(List<string> listKey)
        {
            List<string> newKeys = listKey.ToList();
            return database.StringGet(ConvertRedisKeys(newKeys));
        }

        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T StringGet<T>(string key) where T : class
        {
            return ConvertObject<T>(database.StringGet(key));
        }

        /// <summary>
        /// 为数字增长val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val">可以为负</param>
        /// <returns>增长后的值</returns>
        public double StringIncrement(string key, double val = 1)
        {
            return database.StringIncrement(key, val);
        }

        /// <summary>
        /// 为数字减少val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val">可以为负</param>
        /// <returns>减少后的值</returns>
        public double StringDecrement(string key, double val = 1)
        {
            return database.StringDecrement(key, val);
        }

        

        #endregion String

        #region Hash

        

        /// <summary>
        /// 判断某个数据是否已经被缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public bool HashExists(string key, string dataKey)
        {
            return database.HashExists(key, dataKey);
        }

        /// <summary>
        /// 存储数据到hash表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public bool HashSet<T>(string key, string dataKey, T t, TimeSpan? expire = null)
        {
            if (string.IsNullOrWhiteSpace(dataKey))
            {
                Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
                PropertyInfo[] PropertyList = t.GetType().GetProperties();
                foreach (PropertyInfo prop in PropertyList)
                {
                    string name = prop.Name;
                    var value = prop.GetValue(t, null);
                    keyValuePairs.Add(prop.Name, (value == null) ? "" : value.ToString());
                }
                HashMultiSet(key, keyValuePairs, expire);
                return true;
            }
            else
            {
                string json = ConvertJson(t);
                return database.HashSet(key, dataKey, json);
            }
        }

        /// <summary>
        /// 存储数据到hash表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public void HashMultiSet(string key, Dictionary<string, string> values, TimeSpan? expire = null)
        {
            var fields = values.Select(pair => new HashEntry(pair.Key, pair.Value)).ToArray();
            database.HashSet(key, fields);
            if (expire != null)
            {
                KeyExpire(key, expire);
            }
        }

        //db.HashSet(key, new HashEntry[] { new HashEntry("b", "2"), new HashEntry("c", "3") });

        /// <summary>
        /// 移除hash中的某值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public bool HashDelete(string key, string dataKey)
        {
            return database.HashDelete(key, dataKey);
        }

        /// <summary>
        /// 移除hash中的多个值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKeys"></param>
        /// <returns></returns>
        public long HashDelete(string key, List<RedisValue> dataKeys)
        {
            return database.HashDelete(key, dataKeys.ToArray());
        }

        /// <summary>
        /// 从hash表获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public T HashGet<T>(string key, string dataKey) where T : class
        {
            string value = database.HashGet(key, dataKey);
            return ConvertObject<T>(value);
        }
        /// <summary>
        /// 以字符串的形式获取hash对象中某个字段值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public string HashGet(string key, string dataKey)
        {
            string value = database.HashGet(key, dataKey);
            return value;
        }

        /// <summary>
        /// 从hash表获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T HashGetAll<T>(string key) where T : class
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            HashEntry[] entries = database.HashGetAll(key);
            if (entries != null && entries.Length > 0)
            {
                foreach (var item in entries)
                {
                    if (!item.Value.IsNullOrEmpty)
                    {
                        result.Add(item.Name, item.Value);
                    }
                }
                return JsonHelper.DeserializeObject<T>(JsonHelper.SerializeObject(result));
            }
            return null;
        }
        /// <summary>
        /// 以字典的形式返回哈希对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public Dictionary<string,string> HashGetAll(string key)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            HashEntry[] entries = database.HashGetAll(key);
            if (entries != null && entries.Length > 0)
            {
                foreach (var item in entries)
                {
                    if (!item.Value.IsNullOrEmpty)
                    {
                        result.Add(item.Name, item.Value);
                    }
                }               
            }
            return result;
        }
        /// <summary>
        /// 为数字增长val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>增长后的值</returns>
        public double HashIncrement(string key, string dataKey, double val = 1)
        {
            return database.HashIncrement(key, dataKey, val);
        }

        /// <summary>
        /// 为数字减少val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>减少后的值</returns>
        public double HashDecrement(string key, string dataKey, double val = 1)
        {
            return database.HashDecrement(key, dataKey, val);
        }

        /// <summary>
        /// 根据key获取所有filed 名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<T> HashKeys<T>(string key) where T : class
        {
            RedisValue[] values = database.HashKeys(key);
            return ConvertList<T>(values);
        }

        


        #endregion Hash

        #region List

        

        /// <summary>
        /// 移除指定ListId的内部List的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListRemove<T>(string key, T value) where T : class
        {
            database.ListRemove(key, ConvertJson(value));
        }

        /// <summary>
        /// 获取指定key的List
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<T> ListRange<T>(string key) where T : class
        {
            return ConvertList<T>(database.ListRange(key));
        }

        /// <summary>
        /// 入队
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListRightPush<T>(string key, T value)
        {
            database.ListRightPush(key, ConvertJson(value));
        }

        /// <summary>
        /// 出队
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T ListRightPop<T>(string key) where T : class
        {
            return ConvertObject<T>(database.ListRightPop(key));
        }

        /// <summary>
        /// 入栈
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListLeftPush<T>(string key, T value)
        {
            database.ListLeftPush(key, ConvertJson(value));
        }

        /// <summary>
        /// 出栈
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T ListLeftPop<T>(string key) where T : class
        {
            return ConvertObject<T>(database.ListLeftPop(key));
        }

        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long ListLength(string key)
        {
            return database.ListLength(key);
        }

        


        #endregion List

        #region SortedSet 有序集合

        

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score"></param>
        public bool SortedSetAdd<T>(string key, T value, double score)
        {
            return database.SortedSetAdd(key, ConvertJson<T>(value), score);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public bool SortedSetRemove<T>(string key, T value)
        {
            return database.SortedSetRemove(key, ConvertJson(value));
        }

        /// <summary>
        /// 获取全部
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>        
        public List<T> SortedSetRangeByRank<T>(string key, int page = 1, int limit = 10, bool desc = false) where T : class
        {
            var values = database.SortedSetRangeByRank(key, (page - 1) * limit, ((page - 1) * limit) +(limit-1), (desc == true) ? Order.Descending : Order.Ascending);
            return ConvertList<T>(values);
        }

        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long SortedSetLength(string key)
        {
            return database.SortedSetLength(key);
        }

        

        #endregion SortedSet 有序集合

        #region key

        /// <summary>
        /// 删除单个key
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public bool KeyDelete(string key)
        {
            return database.KeyDelete(key);
        }

        /// <summary>
        /// 删除多个key
        /// </summary>
        /// <param name="keys">rediskey</param>
        /// <returns>成功删除的个数</returns>
        public long KeyDelete(List<string> keys)
        {
            List<string> newKeys = keys.ToList();
            return database.KeyDelete(ConvertRedisKeys(newKeys));
        }

        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public bool KeyExists(string key)
        {
            return database.KeyExists(key);
        }

        /// <summary>
        /// 重新命名key
        /// </summary>
        /// <param name="key">就的redis key</param>
        /// <param name="newKey">新的redis key</param>
        /// <returns></returns>
        public bool KeyRename(string key, string newKey)
        {
            return database.KeyRename(key, newKey);
        }

        /// <summary>
        /// 设置Key的时间
        /// </summary>
        /// <param name="key">redis key</param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool KeyExpire(string key, TimeSpan? expiry = default(TimeSpan?))
        {
            return database.KeyExpire(key, expiry);
        }

        /// <summary>
        /// 模糊匹配Key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<string> Keys(string key)
        {
            var result = (string[])database.ScriptEvaluate(LuaScript.Prepare(
                "return redis.call('KEYS',@keypattern)"), new { keypattern = key });
            return result.ToList();
        }

        /// <summary>
        /// 模糊匹配Key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<List<string>> KeysAsync(string key)
        {
            var result = (string[])await database.ScriptEvaluateAsync(LuaScript.Prepare(
                "return redis.call('KEYS',@keypattern)"), new { keypattern = key });
            return result.ToList();
        }


        /// <summary>
        /// 使用SCAN模糊匹配Key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<string> KeysScan(string key)
        {
            var result = (string[])database.ScriptEvaluate(
                LuaScript.Prepare("local dbsize=redis.call('dbsize') local res=redis.call('scan',0,'match',KEYS[1],'count',dbsize) return res[2]"),
                new RedisKey[] { key });
            return result.ToList();
        }

        /// <summary>
        /// 使用SCAN模糊匹配Key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<List<string>> KeysScanAsync(string key)
        {
            var result = (string[])await database.ScriptEvaluateAsync(
                LuaScript.Prepare("local dbsize=redis.call('dbsize') local res=redis.call('scan',0,'match',KEYS[1],'count',dbsize) return res[2]"),
                new RedisKey[] { key });
            return result.ToList();
        }

        #endregion key

        #region 发布订阅

        /// <summary>
        /// Redis发布订阅  订阅
        /// </summary>
        /// <param name="subChannel"></param>
        /// <param name="handler"></param>
        public void Subscribe(string subChannel, Action<RedisChannel, RedisValue> handler = null)
        {
            ISubscriber sub = Instance.GetSubscriber();
            sub.Subscribe(subChannel, (channel, message) =>
            {
                if (handler == null)
                {
                    //Console.WriteLine(subChannel + " 订阅收到消息：" + message);
                }
                else
                {
                    handler(channel, message);
                }
            });
        }

        /// <summary>
        /// Redis发布订阅  发布
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="channel"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public long Publish<T>(string channel, T msg)
        {
            ISubscriber sub = Instance.GetSubscriber();
            return sub.Publish(channel, ConvertJson(msg));
        }

        /// <summary>
        /// Redis发布订阅  取消订阅
        /// </summary>
        /// <param name="channel"></param>
        public void Unsubscribe(string channel)
        {
            ISubscriber sub = Instance.GetSubscriber();
            sub.Unsubscribe(channel);
        }

        /// <summary>
        /// Redis发布订阅  取消全部订阅
        /// </summary>
        public void UnsubscribeAll()
        {
            ISubscriber sub = Instance.GetSubscriber();
            sub.UnsubscribeAll();
        }

        #endregion 发布订阅

        #region 其他

        public ITransaction CreateTransaction()
        {
            return database.CreateTransaction();
        }

        public IDatabase GetDatabase()
        {
            return database;
        }
        #endregion 其他

        #region 辅助方法
        private string ConvertJson<T>(T value)
        {
            string result = value is string ? value.ToString() : JsonHelper.SerializeObject(value);
            return result;
        }

        private T ConvertObject<T>(RedisValue value) where T : class
        {
            if (value.HasValue)
            {
                return JsonHelper.DeserializeObject<T>(value.ToString());
            }
            return default(T);
        }

        private List<T> ConvertList<T>(RedisValue[] values) where T : class
        {
            List<T> result = new List<T>();
            foreach (var item in values)
            {
                if (item.HasValue && !item.StartsWith("{") && !item.StartsWith("["))
                {
                    result.Add((T)Convert.ChangeType(item, typeof(T)));
                }
                else
                {
                    var model = ConvertObject<T>(item);
                    result.Add(model);
                }
            }
            return result;
        }

        private RedisKey[] ConvertRedisKeys(List<string> redisKeys)
        {
            return redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();
        }

        public void Dispose()
        {
            if (redisConfig.Instance.IsConnected)
                redisConfig.Instance.Close();
            redisConfig.Instance.Dispose();
        }



        #endregion 辅助方法
    }
}
